/*
 * Copyright 2011 Angel Sanadinov
 *
 * This file is part of VBox WMI.
 *
 * VBox WMI is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * VBox WMI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with VBox WMI.  If not, see <http://www.gnu.org/licenses/>.
 */

package Managers.Utilities;

import Utilities.Constants;
import Utilities.Interfaces.SelfValidating;
import com.sun.xml.ws.commons.virtualbox_3_2.ISession;
import com.sun.xml.ws.commons.virtualbox_3_2.IVirtualBox;
import com.sun.xml.ws.commons.virtualbox_3_2.IWebsessionManager;
import java.util.ArrayList;
import javax.xml.ws.WebServiceException;

/**
 * This class represents a connection to the web service of a VirtualBox server. <br><br>
 *
 * Each connection object stores a list of users that have requested to use the
 * connection. When this list becomes empty, the connection is terminated and the
 * connection is established when a user requests to use the connection.
 *
 * @author Angel Sanadinov
 */
public class VBoxConnection implements SelfValidating
{
    private IWebsessionManager vboxSessionManager;  //web sessions manager for logon/logoff
    private IVirtualBox virtualBox;                 //reference to the IVirtualBox of the server
    private String webServiceUsername;              //username for the web service of the server
    private String webServicePassword;              //password for the web service of the server
    private ArrayList<Integer> connectionUsers;     //list of users utilizing the connection

    /**
     * Constructs a new connection object using the specified data. <br><br>
     *
     * <b>Note:</b> <i>The URL should be in the form: <code>http://IP_ADDRESS:PORT</code>.</i>
     *
     * @param webServiceURL the URL to the web service of the user
     * @param webServiceUsername the username used for the authentication with the web service
     * @param webServicePassword the password used for the authentication with the web service
     * @param initialUsersListCapacity initial capacity of the internal <code>ArrayList</code>
     */
    public VBoxConnection(String webServiceURL, String webServiceUsername, 
            String webServicePassword, int initialUsersListCapacity)
    {
        this.vboxSessionManager = new IWebsessionManager(webServiceURL); //creates the new session manager
        this.webServiceUsername = webServiceUsername;
        this.webServicePassword = webServicePassword;
        this.connectionUsers = new ArrayList<Integer>(initialUsersListCapacity);
        this.virtualBox = null;
    }

    /**
     * Registers the user with the specified id in the connection users list.
     *
     * @param userId the id of the user
     * @return <code>true</code> if the operation was successful or <code>false</code>
     *         otherwise
     */
    private boolean registerConnectionUser(int userId)
    {
        //check if the specified user id is valid
        if(userId > Constants.INVALID_USER_ID)
        {
            //determines whether the user is already in the list
            if(!connectionUsers.contains(userId))
                connectionUsers.add(userId); //adds the user to the list
            else
                ;

            return true;
        }
        else
            return false;
    }

    /**
     * Unregisters the user with the specified id from the connection users list. <br><br>
     *
     * <b>Note:</b> <i>This method should only be called by the connections manager!</i>
     *
     * @param userId the id of the user to be unregistered
     */
    public void unregisterConnectionUser(int userId)
    {
        //checks if the specified user is in the list
        if(connectionUsers.contains(userId))
            connectionUsers.remove((Integer)userId); //removes the user from the list
        else
            ;

        //checks if the list is empty
        if(connectionUsers.isEmpty())
            logout();//terminates the connection
        else
            ;
    }

    /**
     * Logs off the VirtualBox server and terminates the connection.
     */
    public void logout()
    {
        try
        {
            //checks if the virtualbox object is available
            if(virtualBox != null)
                vboxSessionManager.logoff(virtualBox); //logs off
            else
                ;//no connection should be available and if there is, there is
                 //nothing that can be done w/o the virtualbox object
        }
        catch(WebServiceException e){}
        finally
        {
            connectionUsers.clear(); //clears the users list
            virtualBox = null;       //removes IVirtualBox reference
        }
    }

    /**
     * Retrieves a reference to the <code>IVirtualBox</code> object associated with
     * the current connection. <br><br>
     * <b>Note:</b> <i>A connection to the VirtualBox server is established if that
     *              has not been done yet.</i>
     *
     * @param userId the id of the user that requested the object
     * @return the <code>IVirtualBox</code> object
     */
    public IVirtualBox getVirtualBoxReference(int userId)
    {
        //if the user is registered and the connection is established...
        if(registerConnectionUser(userId) && isConnected())
            return virtualBox; //...returns the IVirtualBox object
        else
            return null;
    }

    /**
     * Retrieves a reference to the <code>ISession</code> object associated with
     * the current connection. <br><br>
     * <b>Note:</b> <i>A connection to the VirtualBox server is established if that
     *              has not been done yet.</i>
     *
     * @param userId the id of the user that requested the object
     * @return the <code>ISession</code> object
     */
    public ISession getSessionReference(int userId)
    {
        //if the user is registered and the connection is established...
        if(registerConnectionUser(userId) && isConnected())
            return getSessionReference(); //...retrieves the ISession object
        else
            return null;
    }

    /**
     * Retrieves a reference to the <code>ISession</code> object associated with
     * the current connection.
     *
     * @return the <code>ISession</code> object
     */
    //@pre: virtualBox object is not null!
    private ISession getSessionReference()
    {
        ISession result = null;

        try
        {
            //attempts to retrieve the ISession object
            result = vboxSessionManager.getSessionObject(virtualBox);
        }
        catch(WebServiceException e)
        {
            result = null;
        }

        return result;
    }

    /**
     * Checks if the connection is established and attempts to reconnect if it is not.
     *
     * @return <code>true</code> if the connection is active or <code>false</code>
     *         otherwise
     */
    public boolean isConnected()
    {
        boolean result = false;
        
        if(virtualBox != null)
        {
            try
            {
                //checks if the connection and the data object are valid
                virtualBox.getHomeFolder();
                result = true;
            }
            catch(WebServiceException e)
            {
                //attempts to reconnect to the server
                result = establishConnection();
            }
        }
        else
            result = establishConnection(); //attempts to reconnect

        return result;
    }

    /**
     * Attempts to establish a connection to the VirtualBox server.
     *
     * @return <code>true</code> if a connection was established or <code>false</code>
     *         otherwise
     */
    private boolean establishConnection()
    {
        boolean result = false;

        try
        {
            //logs in the server
            virtualBox = vboxSessionManager.logon(webServiceUsername, webServicePassword);
            result = true;
        }
        catch(Exception e)
        {
            result = false;
            connectionUsers.clear();
        }
        
        return result;
    }

    /**
     * Checks the validity of the object. <br><br>
     *
     * This object is considered valid only if the associated <code>IWebsessionManager</code>
     * is valid and if the list of users is initialized.
     *
     * @return <code>true</code> if the object is valid or <code>false</code> otherwise
     */
    @Override
    public boolean isValid()
    {
        if(vboxSessionManager != null && connectionUsers != null)
            return true;
        else
            return false;
    }
}
